/**
 * 用户会话录制工具类
 * 基于rrweb技术实现用户操作的录制和回放功能
 * 包含控制台信息记录和异常捕获功能
 */
class SessionReplayRecorder {
  /**
   * 构造函数 - 初始化录制器
   */
  constructor() {
    // 录制相关状态
    this.events = [] // 存储录制的事件数据
    this.stopFn = null // rrweb停止录制函数
    this.isRecording = false // 是否正在录制
    this.isPlaying = false // 是否正在回放
    this.replayer = null // rrweb回放器实例
    this.startTime = null // 录制开始时间
    this.recordTimer = null // 录制计时器
    this.player = null // rrwebPlayer实例

    // 控制台日志记录
    this.consoleLogs = [] // 存储控制台日志
    this.originalConsole = {} // 原始console方法备份
    this.errorLogs = [] // 存储错误日志
    
    // DOM元素引用
    this.startBtn = null
    this.stopBtn = null
    this.clearBtn = null
    this.playBtn = null
    this.pauseBtn = null
    this.recordStatus = null
    this.playStatus = null
    this.eventCount = null
    this.recordDuration = null
    this.replayContainer = null
    this.replayTarget = null

    // 初始化DOM元素和事件绑定
    this.initializeElements()
    this.bindEvents()
    this.initializeTestArea()
    this.initializeErrorCapture()
  }

  /**
   * 初始化DOM元素引用
   * 获取页面中的控制按钮和状态显示元素
   */
  initializeElements() {
    // 控制按钮
    this.startBtn = document.getElementById('startBtn')
    this.stopBtn = document.getElementById('stopBtn')
    this.clearBtn = document.getElementById('clearBtn')
    this.playBtn = document.getElementById('playBtn')
    this.pauseBtn = document.getElementById('pauseBtn')
    this.showDataBtn = document.getElementById('showDataBtn')

    // 状态显示元素
    this.recordStatus = document.getElementById('recordStatus')
    this.playStatus = document.getElementById('playStatus')
    this.eventCount = document.getElementById('eventCount')
    this.recordDuration = document.getElementById('recordDuration')

    // 回放相关元素
    this.replayContainer = document.getElementById('replayContainer')
    this.replayTarget = document.getElementById('replayTarget')
  }

  /**
   * 绑定事件监听器
   * 为控制按钮添加点击事件处理函数
   */
  bindEvents() {
    this.startBtn.addEventListener('click', () => this.startRecording()) // 开始录制
    this.stopBtn.addEventListener('click', () => this.stopRecording()) // 停止录制
    this.clearBtn.addEventListener('click', () => this.clearData()) // 清空数据
    this.playBtn.addEventListener('click', () => this.playRecording()) // 播放回放
    this.pauseBtn.addEventListener('click', () => this.pauseRecording()) // 暂停回放
    this.showDataBtn.addEventListener('click', () => this.showRecordingData()) // 显示录制数据
  }

  /**
   * 初始化错误捕获
   * 设置全局错误监听器来捕获JavaScript异常
   */
  initializeErrorCapture() {
    // 捕获JavaScript运行时错误
    window.addEventListener('error', (event) => {
      const errorInfo = {
        timestamp: Date.now(),
        type: 'javascript_error',
        message: event.message,
        filename: event.filename,
        lineno: event.lineno,
        colno: event.colno,
        stack: event.error ? event.error.stack : null,
        url: window.location.href
      }
      this.errorLogs.push(errorInfo)
      console.error('捕获到JavaScript错误:', errorInfo)
    })

    // 捕获Promise未处理的rejection
    window.addEventListener('unhandledrejection', (event) => {
      const errorInfo = {
        timestamp: Date.now(),
        type: 'unhandled_promise_rejection',
        reason: event.reason,
        promise: event.promise,
        url: window.location.href
      }
      this.errorLogs.push(errorInfo)
      console.error('捕获到未处理的Promise rejection:', errorInfo)
    })

    // 捕获资源加载错误
    window.addEventListener('error', (event) => {
      if (event.target !== window) {
        const errorInfo = {
          timestamp: Date.now(),
          type: 'resource_error',
          element: event.target.tagName,
          source: event.target.src || event.target.href,
          url: window.location.href
        }
        this.errorLogs.push(errorInfo)
        console.error('捕获到资源加载错误:', errorInfo)
      }
    }, true)
  }

  /**
   * 开始拦截控制台输出
   * 保存原始console方法并替换为自定义方法以记录日志
   */
  startConsoleCapture() {
    // 备份原始console方法
    this.originalConsole = {
      log: console.log,
      warn: console.warn,
      error: console.error,
      info: console.info,
      debug: console.debug
    }

    // 创建拦截器函数
    const createInterceptor = (level, originalMethod) => {
      return (...args) => {
        // 记录日志信息
        const timestamp = Date.now() - (this.startTime || Date.now())
        const logEntry = {
          timestamp,
          level,
          args: args.map(arg => {
            try {
              // 尝试序列化参数
              if (typeof arg === 'object' && arg !== null) {
                return JSON.stringify(arg, null, 2)
              }
              return String(arg)
            } catch (e) {
              return '[无法序列化的对象]'
            }
          }),
          message: args.join(' ')
        }
        
        this.consoleLogs.push(logEntry)
        
        // 调用原始方法保持正常输出
        originalMethod.apply(console, args)
      }
    }

    // 替换console方法
    console.log = createInterceptor('log', this.originalConsole.log)
    console.warn = createInterceptor('warn', this.originalConsole.warn)
    console.error = createInterceptor('error', this.originalConsole.error)
    console.info = createInterceptor('info', this.originalConsole.info)
    console.debug = createInterceptor('debug', this.originalConsole.debug)

    console.log('控制台输出拦截已启动')
  }

  /**
   * 停止拦截控制台输出
   * 恢复原始的console方法
   */
  stopConsoleCapture() {
    // 恢复原始console方法
    if (this.originalConsole.log) {
      console.log = this.originalConsole.log
      console.warn = this.originalConsole.warn
      console.error = this.originalConsole.error
      console.info = this.originalConsole.info
      console.debug = this.originalConsole.debug
    }

    console.log('控制台输出拦截已停止，共记录', this.consoleLogs.length, '条日志')
  }

  /**
   * 显示捕获的控制台日志
   * 在回放时显示录制期间的控制台输出
   */
  displayConsoleLogs() {
    if (this.consoleLogs.length > 0) {
      console.group('录制期间的控制台日志 (' + this.consoleLogs.length + '条)')
      this.consoleLogs.forEach(log => {
        const method = this.originalConsole[log.level] || console.log
        method(`[${this.formatTime(log.timestamp)}] ${log.message}`)
      })
      console.groupEnd()
    }

    if (this.errorLogs.length > 0) {
      console.group('录制期间的错误日志 (' + this.errorLogs.length + '条)')
      this.errorLogs.forEach(error => {
        console.error(`[${this.formatTime(error.timestamp)}] ${error.type}:`, error)
      })
      console.groupEnd()
    }
  }

  /**
   * 开始录制用户操作
   * 使用rrweb库开始记录页面上的所有用户交互
   */
  startRecording() {
    // 防止重复录制
    if (this.isRecording) return

    try {
      // 清空之前的数据
      this.events = []
      this.consoleLogs = []
      this.errorLogs = []
      this.startTime = Date.now()

      // 开始控制台捕获
      this.startConsoleCapture()

      // 开始rrweb录制
      this.stopFn = rrweb.record({
        emit: (event) => {
          // 将事件添加到事件数组中
          this.events.push(event)
        },
        // 录制配置选项
        checkoutEveryNms: 10 * 1000, // 每10秒创建一个检查点
        checkoutEveryNth: 200, // 每200个事件创建一个检查点
        blockClass: 'rr-block', // 阻止录制的CSS类名
        blockSelector: null, // 阻止录制的选择器
        ignoreClass: 'rr-ignore', // 忽略的CSS类名
        maskTextClass: 'rr-mask', // 文本遮罩CSS类名
        maskTextSelector: null, // 文本遮罩选择器
        maskAllInputs: false, // 是否遮罩所有输入
        maskInputOptions: {}, // 输入遮罩选项
        slimDOMOptions: {}, // DOM精简选项
        recordCanvas: true, // 录制canvas
        collectFonts: true, // 收集字体信息
        inlineStylesheet: true, // 内联样式表
        inlineImages: true, // 内联图片为base64，避免blob URL问题
        dataURLOptions: {
          type: 'image/webp', // 图片格式
          quality: 0.8 // 图片质量
        },
        plugins: [], // 插件配置
        // 采样配置
        sampling: {
          mousemove: true, // 录制鼠标移动
          mouseInteraction: true, // 录制鼠标交互
          scroll: 250, // 滚动事件采样间隔(ms)
          media: 800, // 媒体事件采样间隔(ms)
          input: 'last' // 输入事件采样策略
        },
        packFn: rrweb.pack
      })

      // 更新录制状态
      this.isRecording = true
      this.updateUI('recording')
      this.startRecordTimer()

      console.log('开始录制用户操作')
    } catch (error) {
      console.error('录制启动失败:', error)
      this.updateUI('error')
      alert('录制启动失败: ' + error.message)
    }
  }

  /**
   * 停止录制用户操作
   * 停止rrweb录制并保存录制数据
   */
  stopRecording() {
    // 检查是否正在录制
    if (!this.isRecording) return

    try {
      // 停止rrweb录制
      if (this.stopFn) {
        this.stopFn()
        this.stopFn = null
      }

      // 停止控制台捕获
      this.stopConsoleCapture()

      // 更新录制状态
      this.isRecording = false
      this.updateUI('stopped')
      this.stopRecordTimer()

      console.log('录制已停止，共记录', this.events.length, '个事件')
      console.log('控制台日志:', this.consoleLogs.length, '条')
      console.log('错误日志:', this.errorLogs.length, '条')
    } catch (error) {
      console.error('停止录制失败:', error)
      this.updateUI('error')
    }
  }

  /**
   * 清除录制数据
   * 停止录制并清除所有已记录的数据
   */
  clearData() {
    // 如果正在录制，先停止录制
    if (this.isRecording) {
      this.stopRecording()
    }

    // 如果正在播放，先暂停播放
    if (this.isPlaying) {
      this.pauseRecording()
    }

    // 清空事件数据
    this.events = []
    this.consoleLogs = []
    this.errorLogs = []
    this.updateUI('cleared')
    this.replayContainer.style.display = 'none'

    console.log('录制数据已清空')
  }

  /**
   * 播放录制的用户操作
   * 使用rrweb播放器回放之前录制的用户交互
   */
  async playRecording() {
    // 检查是否有录制数据
    if (this.events.length === 0) {
      alert('没有录制数据，请先进行录制')
      return
    }

    // 防止重复播放
    if (this.isPlaying) return

    try {
      // 更新播放状态
      this.isPlaying = true
      this.updateUI('playing')
      this.replayContainer.style.display = 'block'

      // 清空回放容器，准备新的播放内容
      this.replayTarget.innerHTML = ''

      // 检查rrwebPlayer是否已加载
      if (typeof rrwebPlayer === 'undefined') {
        throw new Error('rrwebPlayer未加载，请检查网络连接')
      }

      // 创建rrwebPlayer实例
      this.player = new rrwebPlayer({
        target: this.replayTarget,
        props: {
          events: this.events,
          width: this.replayTarget.offsetWidth,
          height: this.replayTarget.offsetHeight - 100,
          autoPlay: true,
          speedOption: [0.5, 1, 2, 4, 8],
          showController: true,
          tags: {},
          skipInactive: true,
          showWarning: false,
          UNSAFE_replayCanvas: true,
          mouseTail: {
            duration: 500,
            lineCap: 'round',
            lineWidth: 3,
            strokeStyle: 'red',
          },
          insertStyleRules: [
            '.rr-player { width: 100% !important; height: 100% !important; }',
            '.rr-player iframe { width: 100% !important; height: calc(100% - 40px) !important; }',
          ],
        },
      })

      console.log('rrwebPlayer已创建，事件数量:', this.events.length)

      // 显示捕获的控制台日志和错误
      this.displayConsoleLogs()

      // 监听播放完成事件
      setTimeout(() => {
        const playerElement = this.replayTarget.querySelector('.rr-player')
        if (playerElement) {
          playerElement.addEventListener('ended', () => {
            this.isPlaying = false
            this.updateUI('finished')
            console.log('回放已完成')
          })
        }
      }, 1000)
    } catch (error) {
      console.error('回放启动失败:', error)
      this.isPlaying = false
      this.updateUI('error')
      alert('回放启动失败: ' + error.message + '\n\n请检查控制台获取详细错误信息。')
    }
  }

  /**
   * 暂停录制回放
   * 暂停当前正在播放的录制内容
   */
  pauseRecording() {
    if (!this.isPlaying) return

    try {
      this.isPlaying = false
      this.updateUI('paused')
      console.log('回放已暂停')
    } catch (error) {
      console.error('暂停回放失败:', error)
    }
  }

  /**
   * 启动录制计时器
   * 显示录制时长
   */
  startRecordTimer() {
    this.recordTimer = setInterval(() => {
      if (this.startTime) {
        const duration = Date.now() - this.startTime
        this.recordDuration.textContent = this.formatTime(duration)
      }
    }, 1000)
  }

  /**
   * 停止录制计时器
   * 清除定时器并重置引用
   */
  stopRecordTimer() {
    if (this.recordTimer) {
      clearInterval(this.recordTimer)
      this.recordTimer = null
    }
  }

  /**
   * 更新用户界面状态
   * 根据当前操作状态更新按钮和状态显示
   * @param {string} state - 当前状态：recording, stopped, playing, paused, finished, cleared, error
   */
  updateUI(state) {
    // 更新事件计数显示
    this.eventCount.textContent = this.events.length

    // 根据不同状态更新按钮状态和状态显示
    switch (state) {
      case 'recording':
        this.startBtn.disabled = true
        this.stopBtn.disabled = false
        this.clearBtn.disabled = true
        this.playBtn.disabled = true
        this.pauseBtn.disabled = true
        this.showDataBtn.disabled = true

        this.recordStatus.className = 'status recording'
        this.recordStatus.textContent = '🔴 正在录制中... 请进行一些操作来测试录制功能'

        this.playStatus.className = 'status'
        this.playStatus.textContent = '录制进行中，停止录制后可进行回放'
        break

      case 'stopped':
        this.startBtn.disabled = false
        this.stopBtn.disabled = true
        this.clearBtn.disabled = false
        this.playBtn.disabled = this.events.length === 0
        this.pauseBtn.disabled = true
        this.showDataBtn.disabled = this.events.length === 0

        this.recordStatus.className = 'status stopped'
        this.recordStatus.textContent = '⏹️ 录制已停止 - 可以开始新的录制或播放已录制内容'

        this.playStatus.className = 'status'
        this.playStatus.textContent = this.events.length > 0 
          ? `✅ 录制完成，共${this.events.length}个事件 - 点击播放按钮开始回放` 
          : '暂无录制数据 - 请先进行录制操作'
        break

      case 'playing':
        this.startBtn.disabled = true
        this.stopBtn.disabled = true
        this.clearBtn.disabled = false
        this.playBtn.disabled = false
        this.pauseBtn.disabled = false
        this.showDataBtn.disabled = false

        this.playStatus.className = 'status playing'
        this.playStatus.textContent = '▶️ 正在回放录制内容...'
        break

      case 'paused':
        this.startBtn.disabled = false
        this.stopBtn.disabled = true
        this.clearBtn.disabled = false
        this.playBtn.disabled = false
        this.pauseBtn.disabled = true
        this.showDataBtn.disabled = false

        this.playStatus.className = 'status'
        this.playStatus.textContent = '⏸️ 回放已暂停'
        break

      case 'finished':
        this.startBtn.disabled = false
        this.stopBtn.disabled = true
        this.clearBtn.disabled = false
        this.playBtn.disabled = false
        this.pauseBtn.disabled = true
        this.showDataBtn.disabled = false

        this.playStatus.className = 'status'
        this.playStatus.textContent = '✅ 回放已完成'
        break

      case 'cleared':
        this.startBtn.disabled = false
        this.stopBtn.disabled = true
        this.clearBtn.disabled = false
        this.playBtn.disabled = true
        this.pauseBtn.disabled = true
        this.showDataBtn.disabled = true

        this.recordStatus.className = 'status'
        this.recordStatus.textContent = '🗑️ 数据已清空 - 准备开始新的录制'

        this.playStatus.className = 'status'
        this.playStatus.textContent = '暂无录制数据 - 请先进行录制操作'

        this.recordDuration.textContent = '00:00'
        break

      case 'error':
        this.startBtn.disabled = false
        this.stopBtn.disabled = true
        this.clearBtn.disabled = false
        this.playBtn.disabled = this.events.length === 0
        this.pauseBtn.disabled = true
        this.showDataBtn.disabled = this.events.length === 0

        this.recordStatus.className = 'status stopped'
        this.recordStatus.textContent = '❌ 操作失败 - 请检查控制台错误信息'
        break
    }
  }

  /**
   * 初始化测试区域
   * 为测试按钮和输入元素添加事件监听器
   */
  initializeTestArea() {
    // 获取测试元素
    const testBtn1 = document.getElementById('testBtn1')
    const testBtn2 = document.getElementById('testBtn2')
    const testBtn3 = document.getElementById('testBtn3')
    const testInput = document.getElementById('testInput')
    const testTextarea = document.getElementById('testTextarea')
    const testSelect = document.getElementById('testSelect')

    // 视频文件选择
    const videoFile = document.getElementById('videoFile')
    const demoVideo = document.getElementById('demoVideo')

    if (videoFile && demoVideo) {
      videoFile.addEventListener('change', (e) => {
        const file = e.target.files[0]
        if (file) {
          const url = URL.createObjectURL(file)
          demoVideo.src = url
          demoVideo.style.display = 'block'
          this.logOperation('视频操作', `选择了视频文件: ${file.name}`)
          console.log('选择了视频文件:', file.name, '大小:', file.size, 'bytes')
        }
      })

      // 视频播放事件
      demoVideo.addEventListener('play', () => {
        this.logOperation('视频操作', '开始播放视频')
        console.log('视频开始播放')
      })

      demoVideo.addEventListener('pause', () => {
        this.logOperation('视频操作', '暂停播放视频')
        console.log('视频暂停播放')
      })

      demoVideo.addEventListener('ended', () => {
        this.logOperation('视频操作', '视频播放结束')
        console.log('视频播放结束')
      })
    }

    // 按钮测试事件
    if (testBtn1) {
      testBtn1.addEventListener('click', () => {
        this.logOperation('按钮点击', '点击了测试按钮1')
        console.log('点击了测试按钮1')
        testBtn1.textContent = '已点击!'
        setTimeout(() => {
          testBtn1.textContent = '点击测试按钮'
        }, 1000)
      })
    }

    if (testBtn2) {
      testBtn2.addEventListener('mouseenter', () => {
        this.logOperation('鼠标悬停', '鼠标进入按钮2')
        console.log('鼠标悬停在按钮2上')
        testBtn2.style.transform = 'scale(1.1)'
      })

      testBtn2.addEventListener('mouseleave', () => {
        this.logOperation('鼠标离开', '鼠标离开按钮2')
        console.log('鼠标离开按钮2')
        testBtn2.style.transform = 'scale(1)'
      })
    }

    if (testBtn3) {
      let pressTimer
      testBtn3.addEventListener('mousedown', () => {
        this.logOperation('鼠标按下', '开始长按按钮3')
        console.log('开始长按按钮3')
        pressTimer = setTimeout(() => {
          this.logOperation('长按事件', '长按按钮3成功')
          console.info('长按按钮3成功')
          testBtn3.textContent = '长按成功!'
          setTimeout(() => {
            testBtn3.textContent = '长按测试'
          }, 1000)
        }, 1000)
      })

      testBtn3.addEventListener('mouseup', () => {
        clearTimeout(pressTimer)
        this.logOperation('鼠标释放', '释放按钮3')
        console.log('释放按钮3')
      })

      testBtn3.addEventListener('mouseleave', () => {
        clearTimeout(pressTimer)
      })
    }

    // 输入框事件
    if (testInput) {
      testInput.addEventListener('input', (e) => {
        this.logOperation('文本输入', `输入框内容: ${e.target.value}`)
        if (e.target.value.length > 0) {
          console.log('用户输入:', e.target.value)
        }
      })

      testInput.addEventListener('focus', () => {
        this.logOperation('焦点事件', '输入框获得焦点')
        console.log('输入框获得焦点')
      })

      testInput.addEventListener('blur', () => {
        this.logOperation('焦点事件', '输入框失去焦点')
        console.log('输入框失去焦点')
      })
    }

    if (testTextarea) {
      testTextarea.addEventListener('input', (e) => {
        this.logOperation(
          '文本输入',
          `文本域内容: ${e.target.value.substring(0, 50)}${
            e.target.value.length > 50 ? '...' : ''
          }`
        )
        if (e.target.value.length > 0) {
          console.log('文本域输入:', e.target.value)
        }
      })
    }

    // 复选框和单选框事件
    const checkboxes = document.querySelectorAll('#testCheck1, #testCheck2')
    const radios = document.querySelectorAll('input[name="testRadio"]')

    checkboxes.forEach((checkbox, index) => {
      checkbox.addEventListener('change', (e) => {
        this.logOperation(
          '复选框',
          `复选框${index + 1} ${e.target.checked ? '选中' : '取消选中'}`
        )
        console.log(`复选框${index + 1}`, e.target.checked ? '选中' : '取消选中')
      })
    })

    radios.forEach((radio) => {
      radio.addEventListener('change', (e) => {
        this.logOperation('单选框', `选择了选项: ${e.target.value}`)
        console.log('单选框选择:', e.target.value)
      })
    })

    // 下拉选择框事件
    if (testSelect) {
      testSelect.addEventListener('change', (e) => {
        this.logOperation(
          '下拉选择',
          `选择了: ${e.target.options[e.target.selectedIndex].text}`
        )
        console.log('用户选择:', e.target.value)
      })
    }

    // 滚动区域事件
    const scrollArea = document.getElementById('scrollArea')
    if (scrollArea) {
      let scrollTimeout
      scrollArea.addEventListener('scroll', (e) => {
        // 防抖处理，避免过多日志
        clearTimeout(scrollTimeout)
        scrollTimeout = setTimeout(() => {
          const scrollTop = e.target.scrollTop
          const scrollHeight = e.target.scrollHeight
          const clientHeight = e.target.clientHeight
          const scrollPercent = Math.round(
            (scrollTop / (scrollHeight - clientHeight)) * 100
          )
          this.logOperation('滚动事件', `滚动位置: ${scrollPercent}%`)
          console.log('滚动位置:', scrollPercent + '%')
        }, 100)
      })
    }

    // 拖拽功能
    this.initializeDragAndDrop()
  }

  /**
   * 初始化拖拽功能
   * 为拖拽元素和目标区域添加拖拽事件处理
   */
  initializeDragAndDrop() {
    const dragItem = document.getElementById('dragItem')
    const dropZone = document.getElementById('dropZone')

    if (dragItem && dropZone) {
      // 拖拽开始
      dragItem.addEventListener('dragstart', (e) => {
        this.logOperation('拖拽事件', '开始拖拽元素')
        console.log('开始拖拽元素')
        dragItem.classList.add('dragging')
        e.dataTransfer.setData('text/plain', '拖拽数据')
      })

      // 拖拽结束
      dragItem.addEventListener('dragend', () => {
        this.logOperation('拖拽事件', '拖拽结束')
        console.log('拖拽结束')
        dragItem.classList.remove('dragging')
      })

      // 拖拽进入目标区域
      dropZone.addEventListener('dragenter', (e) => {
        e.preventDefault()
        this.logOperation('拖拽事件', '拖拽进入目标区域')
        console.log('拖拽进入目标区域')
        dropZone.classList.add('drag-over')
      })

      // 拖拽在目标区域上方
      dropZone.addEventListener('dragover', (e) => {
        e.preventDefault()
      })

      // 拖拽离开目标区域
      dropZone.addEventListener('dragleave', (e) => {
        if (!dropZone.contains(e.relatedTarget)) {
          this.logOperation('拖拽事件', '拖拽离开目标区域')
          console.log('拖拽离开目标区域')
          dropZone.classList.remove('drag-over')
        }
      })

      // 放置到目标区域
      dropZone.addEventListener('drop', (e) => {
        e.preventDefault()
        this.logOperation('拖拽事件', '成功放置到目标区域')
        console.log('成功放置到目标区域')
        dropZone.classList.remove('drag-over')
        dropZone.textContent = '✅ 放置成功!'
        setTimeout(() => {
          dropZone.textContent = '📦 拖拽目标区域'
        }, 2000)
      })
    }
  }

  /**
   * 记录操作日志
   * @param {string} category - 操作类别
   * @param {string} action - 具体操作
   */
  logOperation(category, action) {
    const operationLog = document.getElementById('operationLog')
    if (operationLog) {
      const timestamp = new Date().toLocaleTimeString()
      const logEntry = document.createElement('div')
      logEntry.className = 'log-entry'
      logEntry.innerHTML = `
        <span class="log-time">[${timestamp}]</span>
        <span class="log-action">${category}: ${action}</span>
      `

      // 如果是第一条日志，清除默认提示
      if (
        operationLog.children.length === 1 &&
        operationLog.children[0].tagName === 'P'
      ) {
        operationLog.innerHTML = ''
      }

      operationLog.appendChild(logEntry)

      // 自动滚动到底部
      operationLog.scrollTop = operationLog.scrollHeight

      // 限制日志条数，避免过多占用内存
      if (operationLog.children.length > 100) {
        operationLog.removeChild(operationLog.firstChild)
      }
    }
  }

  /**
   * 格式化时间显示
   * 将毫秒转换为分:秒格式的时间字符串
   * @param {number} ms - 毫秒数
   * @returns {string} 格式化的时间字符串 (MM:SS)
   */
  formatTime(ms) {
    const seconds = Math.floor(ms / 1000)
    const minutes = Math.floor(seconds / 60)
    const remainingSeconds = seconds % 60

    return `${minutes.toString().padStart(2, '0')}:${remainingSeconds
      .toString()
      .padStart(2, '0')}`
  }

  /**
   * 获取录制数据
   * @returns {Object} 包含事件、控制台日志和错误日志的对象
   */
  getRecordingData() {
    return {
      events: this.events,
      consoleLogs: this.consoleLogs,
      errorLogs: this.errorLogs,
      startTime: this.startTime,
      duration: this.startTime ? Date.now() - this.startTime : 0
    }
  }

  /**
   * 下载录制数据文件
   * 将录制的事件数据导出为JSON文件供播放器使用
   */
  showRecordingData() {
    if (this.events.length === 0) {
      alert('没有录制数据可下载，请先进行录制操作')
      return
    }

    const data = {
      events: this.events,
      width: window.innerWidth,
      height: window.innerHeight
    }
    
    const blob = new Blob([JSON.stringify(data, null, 2)], {
      type: 'application/json'
    })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `recording_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)

    alert(`录制数据已下载！\n\n事件总数: ${this.events.length}\n录制时长: ${this.formatTime(Date.now() - this.startTime)}\n\n下载的文件可以在播放器页面中使用。`)
  }

  /**
   * 导出录制数据为JSON文件
   */
  exportRecording() {
    if (this.events.length === 0) {
      alert('没有录制数据可导出')
      return
    }

    const data = this.getRecordingData()
    const blob = new Blob([JSON.stringify(data, null, 2)], {
      type: 'application/json'
    })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `recording_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)

    console.log('录制数据已导出')
  }

  /**
   * 导入录制数据
   * @param {File} file - JSON文件
   */
  async importRecording(file) {
    try {
      const text = await file.text()
      const data = JSON.parse(text)
      
      if (data.events && Array.isArray(data.events)) {
        this.events = data.events
        this.consoleLogs = data.consoleLogs || []
        this.errorLogs = data.errorLogs || []
        this.startTime = data.startTime || Date.now()
        
        this.updateUI('stopped')
        console.log('录制数据导入成功，事件数量:', this.events.length)
      } else {
        throw new Error('无效的录制数据格式')
      }
    } catch (error) {
      console.error('导入录制数据失败:', error)
      alert('导入失败: ' + error.message)
    }
  }
}

/**
 * 检查rrweb库是否已加载完成
 * 使用轮询方式检测库的加载状态，避免在库未加载时初始化失败
 * @returns {Promise} 加载完成时resolve，超时时reject
 */
function checkRrwebLoaded() {
  return new Promise((resolve, reject) => {
    // 检查库是否已经加载完成
    if (typeof rrweb !== 'undefined' && rrweb.record && rrweb.Replayer) {
      console.log('rrweb库加载成功')
      resolve()
    } else {
      let attempts = 0
      const maxAttempts = 50 // 最大尝试次数，5秒超时

      // 设置轮询检查
      const checkInterval = setInterval(() => {
        attempts++
        // 检查库是否加载完成
        if (
          typeof rrweb !== 'undefined' &&
          rrweb.record &&
          rrweb.Replayer
        ) {
          clearInterval(checkInterval)
          console.log('rrweb库加载成功')
          resolve()
        } else if (attempts >= maxAttempts) {
          // 超时处理
          clearInterval(checkInterval)
          reject(new Error('rrweb库加载超时'))
        }
      }, 100) // 每100ms检查一次
    }
  })
}

// 导出类和函数供外部使用
if (typeof module !== 'undefined' && module.exports) {
  module.exports = { SessionReplayRecorder, checkRrwebLoaded }
} else if (typeof window !== 'undefined') {
  window.SessionReplayRecorder = SessionReplayRecorder
  window.checkRrwebLoaded = checkRrwebLoaded
}